/* ----------------------------------------------------------------------
 * File: 		inter.cpp
 * Created by:		Taylor Wood
 * Email:		Tdwood2@asu.edu
 * Student ID:		1202553801
 *
 * Description:
 * This file contains a mimic of a parser which creates the intermediate
 * code.
 * ----------------------------------------------------------------------*/
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <vector>
#include "lexer.hpp"
#include "inter.hpp"
#include "symbolTable.hpp"
#include "semantic.hpp"

vector<Token> myTokens(1); //empty list of tokens
vector<Jump> jumpLoc(0);

Token nextToken() { 
	Token next;
	if(index < myTokens.size() - 1) {
		next = myTokens[index + 1];
	}
	else {
		next = myTokens[index];
	}
	return next;
}

Token currentToken() {
	if(index >= 0)
		return myTokens[index];
	else
		return myTokens[0];
}

Token endToken() {
	return myTokens[myTokens.size()-1];
}

void startFileTemp() {
	freopen("output.tmp", "w", stdout); //initialize file (destroy existing, if applicable)
}

void startFile(const char* filename) {
	freopen(filename, "w", stdout); //initialize file (destroy existing, if applicable)
}

bool increment() {//if cannot increment when we should be able to, report fault
	bool success = false;
	if(index < myTokens.size() - 1) { //only increment if there is another valid token	
		index++; 
		success = true;
	}
	else if(currentToken().line == endToken().line) {
		success = false;
	}
	return success;
}

int main(int argc, char* argv[]) {
	jumpVal = index = 0; //index that will be used to iterate through vector of tokens
	const char* filename = argv[2];
	string line;
	std::ifstream fin;		//input stream
	fin.open(argv[1]);		//open input text file for input operations
	startFileTemp();	//open output stream, redirect standart output to file. COMMENT THIS LINE OUT TO SEND EVERYTHING TO SCREEN FOR DEBUGGING.

	if(fin.is_open()) {
		while(getline(fin, line)) {
			split_lines(line);
		}
		myTokens = getList();
	}

	if(program() && index == myTokens.size() - 1) {
		//freopen("CON","w",stdout); //uncomment for Windows IDE (Visual Studio)
		freopen ("/dev/tty", "a", stdout); //uncomment for g++ 
		cout << "Build successful" << '\n';
	}
	fin.close();
	fclose(stdout);

	fin.open("output.tmp");
	startFile(filename);
	
	cout << print() << endl;
	printJumpLoc();

	if (fin.is_open()) {
		while(getline(fin, line)) 
			cout << line << endl;
	}

	cout << "OPR 0, 0" << endl; //exit

	fin.close();
	fclose(stdout);
	remove("output.tmp");

	return 0;
}

bool program() {
	index = 0;
	var_section();
	// TODO send symbol table to file
	body();
	return true;
}

void var_section() {
	id_list();
	if (currentToken().word == ";") increment();
		//correct
	else {}
		//TODO Error
}

void id_list() {
	if (currentToken().name == "identifier") { // name of a variable
		if (!search(currentToken().word, "integer", "global"))
			insert(currentToken().word, "integer", "global");
		// else insert the id in the symbol table
		increment();
		if (currentToken().word == ",") {
			increment();
			id_list();
		}
		else if (currentToken().word == ";");
			// finish without an error
		else {}
			// TODO exit with an error
	}
}

void body() {
	if (currentToken().word != "{") {
		// TODO exit with an error
	}
	stmt_list();
	if (currentToken().word != "}" || nextToken().word != "}") {
		// TODO exit with an error
	}
}

void stmt_list() {
	stmt();
	// problem, it skips when seeing this }
	if (nextToken().word == "}") {
		increment();
		// final stmt
	}
	else
		stmt_list();
}

void stmt() {
	if (nextToken().word == "switch")
		switch_stmt();
	else if (nextToken().name == "identifier") 
		assign_stmt();
	else if (nextToken().word == "print")
		print_stmt();
	else if (nextToken().word == "while")
		while_stmt();
	else if (nextToken().word == "if")
		if_stmt();
	else {
		// TODO error
	}
}

void assign_stmt() {
	Token var = nextToken();
	if (nextToken().name != "identifier") {
		//TODO error
	}	
	// we don't want to increment the token, so just look ahead
	if (nextToken().word == "=") {
		//TODO error
	} 
	// TODO find a place to increment
	if (myTokens[index + 2].name == "operator" && myTokens[index + 4].word != ";") {
		increment();
		increment();
		expr();
	}
	else {	
		increment();
		increment();
		primary();
	}
	if (nextToken().word != ";") {
		//TODO error
	}
	increment();
	pc++;
	cout << "STO " << var.word << ", 0" << endl;
}

void expr() {
	primary();
	op();
	primary();
	pc++;
	cout << "OPR " << opNum << ", 0" << endl;
}

void primary() {
	increment();
	if (currentToken().name == "identifier") {
		//TODO check to see if the token is in the symbol table
		pc++;
		cout << "LOD " << currentToken().word << ", 0" << endl;
		//else {}
			//TODO error
	}
	else if (currentToken().name == "integer") {
		pc++;
		cout << "LIT " << currentToken().word << ", 0" << endl;
	}
}

void op() {
	increment();
	if (currentToken().word == "+")
		opNum = 2;
	else if (currentToken().word == "-") 
		opNum = 3;
	else if (currentToken().word == "*")
		opNum = 4;
	else if (currentToken().word == "/")
		opNum = 5;
	else {
		//TODO error
	}
}

void print_stmt() {
	if (nextToken().word == "print") increment();
	if (nextToken().name == "identifier") increment();
	pc++;
	cout << "LOD " << currentToken().word << ", 0" << endl;
	
	if (nextToken().word != ";"); //TODO error
	increment();
	pc++;
	cout << "OPR 21, 0" << endl;
}

void while_stmt() {
	if (nextToken().word == "while") {
		// TODO insert the token into the symbol table with the label and pc
		//jumpLoc.push_back(pc + 1);
	}
	int prevJump = ++jumpVal;
	int jmcTo;
	Jump lbl1;
	Jump lbl2;
	lbl1.pc = pc + 1;
	lbl1.jnum = prevJump;
	jumpLoc.push_back(lbl1);
	increment();
	condition();
	increment();
	pc++;
	jmcTo = ++jumpVal;
	cout << "JMC #e" << jmcTo << ", false" << endl; //TODO a new label 2
	body();
	pc++;
	jumpVal++;
	cout << "JMP #e" << prevJump << ", 0" << endl; //TODO new label
	lbl2.pc = pc + 1;
	lbl2.jnum = jmcTo;
	//jumpLoc.insert(jumpLoc.begin() + (jmcTo - 1), pc + 1);
	jumpLoc.push_back(lbl2);
	//TODO insert label 2 into the symbol table
}

void if_stmt() {
	if (nextToken().word == "if") {}
	increment();
	condition();
	increment();
	pc++;
	int jmc = ++jumpVal;
	Jump lbl;
	lbl.jnum = jmc;
	cout << "JMC #e" << jumpVal << ", false"  << endl; //TODO new label, false
	body();
	//if (!jumpLoc.size())
	//	jumpLoc.push_back(pc + 1);
	//else
	lbl.jnum = jmc;
	lbl.pc = pc + 1;
	jumpLoc.push_back(lbl);
	//jumpLoc.insert(jumpLoc.begin() + (jmc - 1), pc + 1);
	//TODO insert into the symbol table
}

void condition() {
	primary();
	relop();
	primary();
	pc++;
	cout << "OPR " << relOp << ", 0" << endl;
}

void relop() {
	increment();
	if (currentToken().word == ">") 
		relOp = 11;
	else if (currentToken().word == "<") 
		relOp = 12;
	else if (currentToken().word == "!=")
		relOp = 13;
	else {
		//TODO error
	}
}

void switch_stmt() {
	string switch_var = "";
	int jVal = ++jumpVal;
	if (nextToken().word == "switch") 
		increment(); 
	if (nextToken().name == "identifier") {
		increment();
		switch_var = currentToken().word; 
	}
	if (nextToken().word == "{")
		increment();
	if (nextToken().word == "case") {
		case_list(switch_var, jVal);
	}
	if (nextToken().word == "default")
		default_case();
	Jump lbl;
	lbl.pc = pc + 1;
	lbl.jnum = jVal;
	jumpLoc.push_back(lbl);
	//jumpLoc.insert(jumpLoc.begin() + (jVal - 1), pc + 1);
	if (currentToken().word == "}"); ///void\]Done
}

void case_list(string switch_var, int jVal) {
	if (nextToken().word == "case") {
		pc++;
		cout << "LOD " << switch_var << ", 0" << endl;
		case_stmt(jVal);
		if (nextToken().word == "case")
			case_list(switch_var, jVal);
	}
	else {} //TODO error
}

void case_stmt(int jVal) {
	int jmp;
	if (nextToken().word == "case")
		increment(); 
	if (nextToken().name == "integer") {
		increment();
		pc++;
		cout << "LIT " << currentToken().word << ", 0" << endl;
	}
	if (nextToken().word == ":") {
		increment();
		pc++;
		cout << "OPR 15, 0" << endl;
		pc++;
		jmp = ++jumpVal;
		cout << "JMC #e" << jmp << ", false" << endl;
	}
	if (nextToken().word == "{") 
		increment();
	body();
	//increment();
	pc++;
	Jump lbl;
	lbl.pc = pc + 1;
	lbl.jnum = jmp;
	jumpLoc.push_back(lbl);
	cout << "JMP #e" << jVal << ", 0"  << endl;
	//jumpLoc.push_back(pc + 1);
}

void default_case() {
	if (nextToken().word == "default") 
		increment(); 	//TODO incr token
	if (nextToken().word == ":")
		increment();	//TODO incr token
	if (nextToken().word == "{")
		increment();
	body();
}

void printJumpLoc() {
	for (int i = 0; i < jumpLoc.size(); i++) {
		cout << "#e" << jumpLoc[i].jnum << " = " << jumpLoc[i].pc;
		if (i + 1 < jumpLoc.size()) cout << ", ";
	}
	if (jumpLoc.size() == 0)
		cout << "#e0 = " << pc + 1 << endl;
	else
		cout << endl;
}
